import * as L from 'leaflet'

L.Marker.Measurement = L[L.Layer ? 'Layer' : 'Class'].extend({
    options: {
        pane: 'markerPane'
    },

    initialize: function(latlng, measurement, title, rotation, options) {
        L.setOptions(this, options);

        this._latlng = latlng;
        this._measurement = measurement;
        this._title = title;
        this._rotation = rotation;
    },

    addTo: function(map) {
        map.addLayer(this);
        return this;
    },

    onAdd: function(map) {
        this._map = map;
        var pane = this.getPane ? this.getPane() : map.getPanes().markerPane;
        var el = this._element = L.DomUtil.create('div', 'leaflet-zoom-animated leaflet-measure-path-measurement', pane);
        var inner = L.DomUtil.create('div', '', el);
        inner.title = this._title;
        inner.innerHTML = this._measurement;

        map.on('zoomanim', this._animateZoom, this);

        this._setPosition();
    },

    onRemove: function(map) {
        map.off('zoomanim', this._animateZoom, this);
        var pane = this.getPane ? this.getPane() : map.getPanes().markerPane;
        pane.removeChild(this._element);
        this._map = null;
    },

    _setPosition: function() {
        L.DomUtil.setPosition(this._element, this._map.latLngToLayerPoint(this._latlng));
        this._element.style.transform += ' rotate(' + this._rotation + 'rad)';
    },

    _animateZoom: function(opt) {
        var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round();
        L.DomUtil.setPosition(this._element, pos);
        this._element.style.transform += ' rotate(' + this._rotation + 'rad)';
    }
});

L.marker.measurement = function(latLng, measurement, title, rotation, options) {
    return new L.Marker.Measurement(latLng, measurement, title, rotation, options);
};

var formatDistance = function(d) {
    var unit,
        feet;

    if (this._measurementOptions.imperial) {
        feet = d / 0.3048;
        if (feet > 3000) {
            d = d / 1609.344;
            unit = 'mi';
        } else {
            d = feet;
            unit = 'ft';
        }
    } else {
        if (d > 1000) {
            d = d / 1000;
            unit = 'km';
        } else {
            unit = 'm';
        }
    }

    if (d < 100) {
        return d.toFixed(1) + ' ' + unit;
    } else {
        return Math.round(d) + ' ' + unit;
    }
}

var formatArea = function(a) {
    var unit,
        sqfeet;

    if (this._measurementOptions.imperial) {
        if (a > 404.685642) {
            a = a / 4046.85642;
            unit = 'ac';
        } else {
            a = a / 0.09290304;
            unit = 'ft²';
        }
    } else {
        if (a > 1000000) {
            a = a / 1000000;
            unit = 'km²';
        } else {
            unit = 'm²';
        }
    }

    if (a < 100) {
        return a.toFixed(1) + ' ' + unit;
    } else {
        return Math.round(a) + ' ' + unit;
    }
}

var RADIUS = 6378137;
// ringArea function copied from geojson-area
// (https://github.com/mapbox/geojson-area)
// This function is distributed under a separate license,
// see LICENSE.md.
var ringArea = function ringArea(coords) {
    var rad = function rad(_) {
        return _ * Math.PI / 180;
    };
    var p1, p2, p3, lowerIndex, middleIndex, upperIndex,
    area = 0,
    coordsLength = coords.length;

    if (coordsLength > 2) {
        for (var i = 0; i < coordsLength; i++) {
            if (i === coordsLength - 2) {// i = N-2
                lowerIndex = coordsLength - 2;
                middleIndex = coordsLength -1;
                upperIndex = 0;
            } else if (i === coordsLength - 1) {// i = N-1
                lowerIndex = coordsLength - 1;
                middleIndex = 0;
                upperIndex = 1;
            } else { // i = 0 to N-3
                lowerIndex = i;
                middleIndex = i+1;
                upperIndex = i+2;
            }
            p1 = coords[lowerIndex];
            p2 = coords[middleIndex];
            p3 = coords[upperIndex];
            area += ( rad(p3.lng) - rad(p1.lng) ) * Math.sin( rad(p2.lat));
        }

        area = area * RADIUS * RADIUS / 2;
    }

    return Math.abs(area);
};
/**
 * Handles the init hook for polylines and circles.
 * Implements the showOnHover functionality if called for.
 */
var addInitHook = function() {
    var showOnHover = this.options.measurementOptions && this.options.measurementOptions.showOnHover;
    if (this.options.showMeasurements && !showOnHover) {
        this.showMeasurements();
    }
    if (this.options.showMeasurements && showOnHover) {
        this.on('mouseover', function() {
            this.showMeasurements();
        });
        this.on('mouseout', function() {
            this.hideMeasurements();
        });
    }
};

var circleArea = function circleArea(d) {
    var rho = d / RADIUS;
    return 2 * Math.PI * RADIUS * RADIUS * (1 - Math.cos(rho));
};

var override = function(method, fn, hookAfter) {
    if (!hookAfter) {
        return function() {
            var originalReturnValue = method.apply(this, arguments);
            var args = Array.prototype.slice.call(arguments)
            args.push(originalReturnValue);
            return fn.apply(this, args);
        }
    } else {
        return function() {
            fn.apply(this, arguments);
            return method.apply(this, arguments);
        }
    }
};

L.Polyline.include({
    showMeasurements: function(options) {
        if (!this._map || this._measurementLayer) return this;

        this._measurementOptions = L.extend({
            showOnHover: (options && options.showOnHover) || false,
            minPixelDistance: 30,
            showDistances: true,
            showArea: true,
            showTotalDistance: true,
            lang: {
                totalLength: 'Total length',
                totalArea: 'Total area',
                segmentLength: 'Segment length'
            }
        }, options || {});

        this._measurementLayer = L.layerGroup().addTo(this._map);
        this.updateMeasurements();

        this._map.on('zoomend', this.updateMeasurements, this);

        return this;
    },

    hideMeasurements: function() {
        if (!this._map) return this;

        this._map.off('zoomend', this.updateMeasurements, this);

        if (!this._measurementLayer) return this;
        this._map.removeLayer(this._measurementLayer);
        this._measurementLayer = null;

        return this;
    },

    onAdd: override(L.Polyline.prototype.onAdd, function(originalReturnValue) {
        var showOnHover = this.options.measurementOptions && this.options.measurementOptions.showOnHover;
        if (this.options.showMeasurements && !showOnHover) {
            this.showMeasurements(this.options.measurementOptions);
        }

        return originalReturnValue;
    }),

    onRemove: override(L.Polyline.prototype.onRemove, function(originalReturnValue) {
        this.hideMeasurements();

        return originalReturnValue;
    }, true),

    setLatLngs: override(L.Polyline.prototype.setLatLngs, function(originalReturnValue) {
        this.updateMeasurements();

        return originalReturnValue;
    }),

    spliceLatLngs: override(L.Polyline.prototype.spliceLatLngs, function(originalReturnValue) {
        this.updateMeasurements();

        return originalReturnValue;
    }),

    formatDistance: formatDistance,
    formatArea: formatArea,

    updateMeasurements: function() {
        if (!this._measurementLayer) return this;

        var latLngs = this.getLatLngs(),
            isPolygon = this instanceof L.Polygon,
            options = this._measurementOptions,
            totalDist = 0,
            formatter,
            ll1,
            ll2,
            p1,
            p2,
            pixelDist,
            dist;

        if (latLngs && latLngs.length && L.Util.isArray(latLngs[0])) {
            // Outer ring is stored as an array in the first element,
            // use that instead.
            latLngs = latLngs[0];
        }

        this._measurementLayer.clearLayers();

        if (this._measurementOptions.showDistances && latLngs.length > 1) {
            formatter = this._measurementOptions.formatDistance || L.bind(this.formatDistance, this);

            for (var i = 1, len = latLngs.length; (isPolygon && i <= len) || i < len; i++) {
                ll1 = latLngs[i - 1];
                ll2 = latLngs[i % len];
                dist = ll1.distanceTo(ll2);
                totalDist += dist;

                p1 = this._map.latLngToLayerPoint(ll1);
                p2 = this._map.latLngToLayerPoint(ll2);

                pixelDist = p1.distanceTo(p2);

                if (pixelDist >= options.minPixelDistance) {
                    L.marker.measurement(
                        this._map.layerPointToLatLng([(p1.x + p2.x) / 2, (p1.y + p2.y) / 2]),
                        formatter(dist), options.lang.segmentLength, this._getRotation(ll1, ll2), options)
                        .addTo(this._measurementLayer);
                }
            }

            // Show total length for polylines
            if (!isPolygon && this._measurementOptions.showTotalDistance) {
                L.marker.measurement(ll2, formatter(totalDist), options.lang.totalLength, 0, options)
                    .addTo(this._measurementLayer);
            }
        }

        if (isPolygon && options.showArea && latLngs.length > 2) {
            formatter = options.formatArea || L.bind(this.formatArea, this);
            var area = ringArea(latLngs);
            L.marker.measurement(this.getBounds().getCenter(),
                formatter(area), options.lang.totalArea, 0, options)
                .addTo(this._measurementLayer);
        }

        return this;
    },

    _getRotation: function(ll1, ll2) {
        var p1 = this._map.project(ll1),
            p2 = this._map.project(ll2);

        return Math.atan((p2.y - p1.y) / (p2.x - p1.x));
    }
});

L.Polyline.addInitHook(function() {
    addInitHook.call(this);
});

L.Circle.include({
    showMeasurements: function(options) {
        if (!this._map || this._measurementLayer) return this;

        this._measurementOptions = L.extend({
            showOnHover: false,
            showArea: true,
            lang: {
                totalArea: 'Total area',
            }
        }, options || {});

        this._measurementLayer = L.layerGroup().addTo(this._map);
        this.updateMeasurements();

        this._map.on('zoomend', this.updateMeasurements, this);

        return this;
    },

    hideMeasurements: function() {
        if (!this._map) return this;

        this._map.on('zoomend', this.updateMeasurements, this);

        if (!this._measurementLayer) return this;
        this._map.removeLayer(this._measurementLayer);
        this._measurementLayer = null;

        return this;
    },

    onAdd: override(L.Circle.prototype.onAdd, function(originalReturnValue) {
        var showOnHover = this.options.measurementOptions && this.options.measurementOptions.showOnHover;
        if (this.options.showMeasurements && !showOnHover) {
            this.showMeasurements(this.options.measurementOptions);
        }

        return originalReturnValue;
    }),

    onRemove: override(L.Circle.prototype.onRemove, function(originalReturnValue) {
        this.hideMeasurements();

        return originalReturnValue;
    }, true),

    setLatLng: override(L.Circle.prototype.setLatLng, function(originalReturnValue) {
        this.updateMeasurements();

        return originalReturnValue;
    }),

    setRadius: override(L.Circle.prototype.setRadius, function(originalReturnValue) {
        this.updateMeasurements();

        return originalReturnValue;
    }),

    formatArea: formatArea,

    updateMeasurements: function() {
        if (!this._measurementLayer) return;

        var latLng = this.getLatLng(),
            options = this._measurementOptions,
            formatter = options.formatArea || L.bind(this.formatArea, this);

        this._measurementLayer.clearLayers();

        if (options.showArea) {
            formatter = options.formatArea || L.bind(this.formatArea, this);
            var area = circleArea(this.getRadius());
            L.marker.measurement(latLng,
                formatter(area), options.lang.totalArea, 0, options)
                .addTo(this._measurementLayer);
        }
    }
})

L.Circle.addInitHook(function() {
    addInitHook.call(this);
});


L.blinkMarker = function (point, property) {
    // 使用js标签,便于操作,这个temDivEle的作用是将divEle通过innerHTML的方式获取为字符串
    var tempDivEle = document.createElement("div");
    var divEle = document.createElement("div");
    var spanEl = document.createElement("span");
    var aEl = document.createElement("a");
    tempDivEle.append(divEle);
    divEle.append(spanEl);
    spanEl.append(aEl);
    // 设置上基础的样式
    spanEl.classList.add("pulse-icon");
    aEl.classList.add("dive-icon");
    // 操作样式
    var style = document.createElement("style");
    style.type = "text/css";
    document.head.appendChild(style);
    // sheet = style.sheet;
    // 主体颜色
    if (property) {
        if (property.color) {
            spanEl.style.backgroundColor = property.color;
            if (!property.diveColor) {
                aEl.style.boxShadow = "0 0 6px 2px " + property.color;
            }
        }
        // 标记大小
        if (property.iconSize) {
            spanEl.style.width = property.iconSize[0] + "px";
            spanEl.style.height = property.iconSize[1] + "px";
        }
        // 发散的color
        if (property.diveColor) {
            // 发散的重度
            if (property.level) {
                aEl.style.boxShadow = "0 0 " + (property.level * 3) + "px " + property.level + "px " + property.diveColor;
            } else {
                aEl.style.boxShadow = "0 0 6px 2px " + property.diveColor;
            }
        }
        // 发散的重度
        if (property.level) {
            if (property.diveColor) {
                aEl.style.boxShadow = "0 0 " + (property.level * 3) + "px " + property.level + "px " + property.diveColor;
            }else if (property.color) {
                aEl.style.boxShadow = "0 0 " + (property.level * 3) + "px " + property.level + "px " + property.color;
            }else{
                aEl.style.boxShadow = "0 0 " + (property.level * 3) + "px " + property.level + "px red";
            }
        }

        // 闪烁的速度
        if (property.speedTime) {
            aEl.style.setProperty("animation", "pulsate " + property.speedTime + "s infinite")
        }
    }
    var myIcon = L.divIcon({ className: 'my-div-icon', html: tempDivEle.innerHTML });
    var marker = L.marker(point, { icon: myIcon, title: property.title });
    return marker;
}
export const leaflet = L
